#ifndef float_h
#define float_h

// this header needs
#include "int.h"

// float_op result struct
typedef struct {
  owl_fmax f;
  owl_bool success;
} owl_float_op_result;

typedef enum {
  OWL_TYPE_FLOAT_OP_RESULT = OWL_TYPE_INT_OP_RESULT + 1,
} owl_type_float_h;

// gcc12/clang14
// +nan -> sign bit 0, all exponent bits in 1, leftmost bit in fraction 1
// -nan -> sign bit 1, all exponent bits in 1, leftmost bit in fraction 1
// +inf -> sign bit 0, all exponent bits in 1, fraction bits in 0
// -inf -> sign bit 1, all exponent bits in 1, fraction bits in 0
#define OWL_PLUS_NAN (0.0f / 0.0f)
#define OWL_MINUS_NAN -(0.0f / 0.0f)
#define OWL_PLUS_INF (1.0f / 0.0f)
#define OWL_MINUS_INF -(1.0f / 0.0f)

// basic math operations/functions
owl_float_op_result owl_abs(owl_fmax num);
owl_float_op_result owl_pow_float_to_sint(owl_fmax base, owl_smax exponent);
owl_float_op_result owl_float_frac(owl_fmax num);
owl_float_op_result owl_float_int(owl_fmax num);
owl_float_op_result owl_floor(owl_fmax num);
owl_float_op_result owl_ceil(owl_fmax num);

// float type constants (bit sizes)
#define OWL_FLOAT_SIGN_SIZE 1
// half
#define OWL_FLOAT_HALF_EXP_SIZE 5
#define OWL_FLOAT_HALF_FRAC_SIZE 10
#define OWL_FLOAT_HALF_SIZE (OWL_FLOAT_SIGN_SIZE + OWL_FLOAT_HALF_EXP_SIZE + OWL_FLOAT_HALF_FRAC_SIZE)
// single
#define OWL_FLOAT_SINGLE_EXP_SIZE 8
#define OWL_FLOAT_SINGLE_FRAC_SIZE 23
#define OWL_FLOAT_SINGLE_SIZE (OWL_FLOAT_SIGN_SIZE + OWL_FLOAT_SINGLE_EXP_SIZE + OWL_FLOAT_SINGLE_FRAC_SIZE)
// double
#define OWL_FLOAT_DOUBLE_EXP_SIZE 11
#define OWL_FLOAT_DOUBLE_FRAC_SIZE 52
#define OWL_FLOAT_DOUBLE_SIZE (OWL_FLOAT_SIGN_SIZE + OWL_FLOAT_DOUBLE_EXP_SIZE + OWL_FLOAT_DOUBLE_FRAC_SIZE)
// owl_fmax
#define OWL_FLOAT_FMAX_SIZE (sizeof(owl_fmax) * OWL_BYTE_BITS) 

struct owl_float_bit_sizes {
  owl_byte sign_size;
  owl_byte exp_size;
  owl_byte frac_size;
} owl_get_float_bit_sizes(owl_umax type_size); // internal usage

// calculate specific float type from bits
// only byte aligned, 16, 32 and 64 bits floats
owl_bool owl_check_float_bitsize(owl_umax bitsize);
owl_float_op_result owl_bits_as_ieee754_float(owl_bitarr * bits);
owl_float_op_result owl_get_float(owl_bitarr * bits);
owl_float_op_result owl_get_std_float(owl_bitarr * bits, owl_endian endian);
owl_bool owl_write_std_float_bitarr(owl_bitarr * src, owl_endian src_endian,
                                    owl_bitarr * dest, owl_endian dest_endian);
owl_bool owl_write_std_float(owl_fmax num, owl_bitarr * dest, owl_endian endian);

// owl_get_std_float() but that increase bits -> arr -> ptr and decreases bits -> arr -> size
// by (bits -> num_bits + bits -> left_pad) / OWL_BYTE_BITS
// they treat the bits structure as a moving window (mw) over byte data
// This window is modified in each call of these functions.
owl_float_op_result owl_get_std_float_mw(owl_bitarr * bits, owl_endian endian);
// get standard specific float functions with moving window
// they override bits -> num_bits
owl_float_op_result owl_get_std_float16_mw(owl_bitarr * bits, owl_endian endian);
owl_float_op_result owl_get_std_float32_mw(owl_bitarr * bits, owl_endian endian);
owl_float_op_result owl_get_std_float64_mw(owl_bitarr * bits, owl_endian endian);

// same as the above but for writing
owl_bool owl_write_std_float_mv(owl_fmax num, owl_bitarr * dest, owl_endian endian);
owl_bool owl_write_std_float16_mv(owl_fmax num, owl_bitarr * dest, owl_endian endian);
owl_bool owl_write_std_float32_mv(owl_fmax num, owl_bitarr * dest, owl_endian endian);
owl_bool owl_write_std_float64_mv(owl_fmax num, owl_bitarr * dest, owl_endian endian);

#include "float.c"

#endif // float_h
